/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.meanzoft.m3d.model.mdx;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.meanzoft.m3d.model.m3dmodel.Face;
import org.meanzoft.m3d.model.m3dmodel.Model;
import org.meanzoft.m3d.model.m3dmodel.Mesh;

/**
 *
 * @author Laptop
 */
public class MDXLoader {

    public static Model load(String fileName) {
        MDXLoader mdxLoader = new MDXLoader(fileName);
        return mdxLoader.model;
    }
    private Model model;
    private MDXHeader header;
    private ByteStream mdxBuffer;
    private Map<String, Method> methods = new HashMap<String, Method>();

    private MDXLoader(String fileName) {

        try {
            model = new Model();
            header = new MDXHeader();
            File file = new File(fileName);
            FileInputStream inStream = new FileInputStream(file);
            byte[] b = new byte[inStream.available()];
            inStream.read(b);
            mdxBuffer = new ByteStream(b);
            registerParameters();
            parseMDXFile();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void parseMDXFile() throws IOException {
        if (!mdxBuffer.getString(4).equalsIgnoreCase("MDLX")) {
            logSevere("File is not an mdx model.");
            return;
        }

        while (mdxBuffer.getRemaining() > 4) {
            String groupType = mdxBuffer.getString(4);
            int groupSize = mdxBuffer.getLEDWORD();
            ByteStream buffer = new ByteStream(mdxBuffer.get(groupSize));
            //logInfo(" #<####># GroupType: " + groupType + " GroupSize: " + groupSize);
            try {
                if (methods.containsKey(groupType)) {
                    methods.get(groupType).invoke(this, buffer);
                } else {
                    logSevere("Unhandled parameter " + groupType);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

    }

    public boolean registerParameters() {
        try {
            registerParameter("VERS", this.getClass().getDeclaredMethod("loadVersion", ByteStream.class));
            registerParameter("MODL", this.getClass().getDeclaredMethod("loadModel", ByteStream.class));
            registerParameter("SEQS", this.getClass().getDeclaredMethod("loadSequences", ByteStream.class));
            registerParameter("GLBS", this.getClass().getDeclaredMethod("loadGlobalSequences", ByteStream.class));
            registerParameter("TEXS", this.getClass().getDeclaredMethod("loadTextures", ByteStream.class));
            registerParameter("MTLS", this.getClass().getDeclaredMethod("loadMaterials", ByteStream.class));
            registerParameter("TXAN", this.getClass().getDeclaredMethod("loadTextureAnimations", ByteStream.class));
            registerParameter("GEOS", this.getClass().getDeclaredMethod("loadGeosets", ByteStream.class));
            registerParameter("GEOA", this.getClass().getDeclaredMethod("loadGeosetAnimations", ByteStream.class));
            registerParameter("BONE", this.getClass().getDeclaredMethod("loadBones", ByteStream.class));
            registerParameter("LITE", this.getClass().getDeclaredMethod("loadLights", ByteStream.class));
            registerParameter("HELP", this.getClass().getDeclaredMethod("loadHelpers", ByteStream.class));
            registerParameter("ATCH", this.getClass().getDeclaredMethod("loadAttachments", ByteStream.class));
            registerParameter("PIVT", this.getClass().getDeclaredMethod("loadPivotPoints", ByteStream.class));
            registerParameter("PREM", this.getClass().getDeclaredMethod("loadParticleEmitters", ByteStream.class));
            registerParameter("PRE2", this.getClass().getDeclaredMethod("loadParticleEmitters2", ByteStream.class));
            registerParameter("RIBB", this.getClass().getDeclaredMethod("loadRibbonEmitters", ByteStream.class));
            registerParameter("EVTS", this.getClass().getDeclaredMethod("loadEventObjects", ByteStream.class));
            registerParameter("CAMS", this.getClass().getDeclaredMethod("loadCameras", ByteStream.class));
            registerParameter("CLID", this.getClass().getDeclaredMethod("loadCollisionShapes", ByteStream.class));
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public void registerParameter(String param, Method action) {
        methods.put(param, action);
    }

    public void loadVersion(ByteStream buffer) throws IOException {
        header.setVersion(buffer.getLEDWORD());
    }

    public void loadModel(ByteStream buffer) throws IOException {
        header.setName(buffer.getString(0));
        buffer.getLEDWORD();

        header.getExtent().setRadius(buffer.getFloat());
        header.getExtent().setMin(new Vector3f(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()));
        header.getExtent().setMax(new Vector3f(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()));
        header.setBlendTime(buffer.getLEDWORD());
    }

    public void loadSequences(ByteStream buffer) throws IOException {
        int unitSize = 132;
        while (buffer.getRemaining() > 0) {
            ModelSequence sequence = new ModelSequence();
            sequence.setName(buffer.getString(0));
            if (sequence.getName() == null) {
                sequence.setName("");
            }

            sequence.setInterval(new Vector2f(buffer.getLEDWORD(), buffer.getLEDWORD()));
            sequence.setMoveSpeed(buffer.getFloat());
            sequence.setNonLooping(buffer.getLEDWORD() == 1);
            sequence.setRarity(buffer.getFloat());

            buffer.getLEDWORD();

            sequence.getExtent().setRadius(buffer.getFloat());
            sequence.getExtent().setMin(new Vector3f(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()));
            sequence.getExtent().setMax(new Vector3f(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()));
            buffer.skip(unitSize - (sequence.getName().length()) - (52));
        }
    }

    public void loadGlobalSequences(ByteStream buffer) throws IOException {

        while (buffer.getRemaining() > 0) {
            //globalSequence.duration = buffer.getLEDWORD();
            buffer.getLEDWORD(); //temp
        }
    }

    public void loadTextures(ByteStream buffer) throws IOException {
        int unitSize = 268;
        while (buffer.getRemaining() > 0) {
            /**
             * Load texture
             */
            int replacebbleId = buffer.getLEDWORD();

            String name = buffer.getString(0);
            if (name == null) {
                name = "";
            }

            buffer.getLEDWORD();

            int flags = buffer.getLEDWORD();
            int wrapWidth = flags & 2;
            int wrapHeight = flags & 3;

            buffer.skip(unitSize - (4 * 3) - name.length());
        }

    }

    public void loadMaterials(ByteStream buffer) throws IOException {
    }

    public void loadTextureAnimations(ByteStream buffer) throws IOException {
    }

    public void loadGeosets(ByteStream buffer) throws IOException {

        while (buffer.getRemaining() > 0) {
            GeoSet gs = new GeoSet();
            GeoSetParser gsp = new GeoSetParser(gs);
            LinkedList<Vector3f> vertices = new LinkedList<Vector3f>();
            LinkedList<Vector3f> normals = new LinkedList<Vector3f>();
            LinkedList<Vector2f> texCoords = new LinkedList<Vector2f>();
            LinkedList<Integer[]> faceList = new LinkedList<Integer[]>();
            Mesh mesh = new Mesh();

            int geoSetSize = buffer.getLEDWORD();
            ByteStream geoBuffer = new ByteStream(buffer.get(geoSetSize - 4));
            if (tagMatch("VRTX", geoBuffer.getString(4))) {
                gsp.parseVertexData(geoBuffer);
                if (tagMatch("NRMS", geoBuffer.getString(4))) {
                   gsp.parseNormalData(geoBuffer);
                    if (tagMatch("PTYP", geoBuffer.getString(4))) {
                        gsp.parseFaceGroupTypeData(geoBuffer);
                        if (tagMatch("PCNT", geoBuffer.getString(4))) {
                            gsp.parseFaceGroupData(geoBuffer);
                            if (tagMatch("PVTX", geoBuffer.getString(4))) {
                                gsp.parseFaceData(geoBuffer);
                                if (tagMatch("GNDX", geoBuffer.getString(4))) {
                                    gsp.parseVertexGroupData(geoBuffer);
                                    if (tagMatch("MTGC", geoBuffer.getString(4))) {
                                        gsp.parseMTGC(geoBuffer);
                                        if (tagMatch("MATS", geoBuffer.getString(4))) {
                                            gsp.parseMatriceData(geoBuffer);
                                            if (tagMatch("UVAS", geoBuffer.getString(4))) {
                                               gsp.parseTextureVertexGroups(geoBuffer);
                                                if (tagMatch("UVBS", geoBuffer.getString(4))) {
                                                   gsp.parseTextureCoordinateData(geoBuffer);
                                                   model.geos.add(gs);
                                                } else {
                                                    logSevere("Corrupt Geoset expected tag UVBS");
                                                }
                                            } else {
                                                logSevere("Corrupt Geoset expected tag UVAS");
                                            }
                                        } else {
                                            logSevere("Corrupt Geoset expected tag MATS");
                                        }
                                    } else {
                                        logSevere("Corrupt Geoset expected tag MTGC");
                                    }
                                } else {
                                    logSevere("Corrupt Geoset expected tag GNDX");
                                }
                            } else {
                                logSevere("Corrupt Geoset expected tag PVTX");
                            }
                        } else {
                            logSevere("Corrupt Geoset expected tag PCNT");
                        }
                    } else {
                        logSevere("Corrupt Geoset expected tag PTYP");
                    }
                } else {
                    logSevere("Corrupt Geoset expected tag NRMS");
                }
            } else {
                logSevere("Corrupt Geoset expected tag VRTX");
            }
        }

    }

    public void loadGeosetAnimations(ByteStream buffer) throws IOException {
    }

    public void loadBones(ByteStream buffer) throws IOException {
    }

    public void loadLights(ByteStream buffer) throws IOException {
    }

    public void loadHelpers(ByteStream buffer) throws IOException {
    }

    public void loadAttachments(ByteStream buffer) throws IOException {
    }

    public void loadPivotPoints(ByteStream buffer) throws IOException {
    }

    public void loadParticleEmitters(ByteStream buffer) throws IOException {
    }

    public void loadParticleEmitters2(ByteStream buffer) throws IOException {
    }

    public void loadRibbonEmitters(ByteStream buffer) throws IOException {
    }

    public void loadEventObjects(ByteStream buffer) throws IOException {
    }

    public void loadCameras(ByteStream buffer) throws IOException {
    }

    public void loadCollisionShapes(ByteStream buffer) throws IOException {
    }

    public void logInfo(Object obj) {
        System.err.println("[MDXLoader]<Info> " + obj);
    }

    public void logSevere(Object obj) {
        System.err.println("[MDXLoader]<Severe> " + obj);
    }

    public boolean tagMatch(String tag, String dword) {
        if (tag.equalsIgnoreCase(dword)) {
            return true;
        }
        return false;
    }
}
